#ifndef GAMEENGINE_CORE_RESOURCE_RESOURCE_HPP
#define GAMEENGINE_CORE_RESOURCE_RESOURCE_HPP

#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//========================================================



//! Constructor
/*!
 @param context The ID associated to the context
 */
template <class RESOURCEID, class CONTEXTID>
ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext::ResourceContext(CONTEXTID context) : context_(context) {}

//! Destructor
template <class RESOURCEID, class CONTEXTID>
ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext::~ResourceContext() {}

//! Reference given resource
/*!
 @param resource The resource to reference in this context
 @return True if resource was referenced, false otherwise
 @note If the resource does not belong to this context, this function always returns false
 */
template <class RESOURCEID, class CONTEXTID>
template <class RESOURCE>
bool ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext::reference_resource(const RESOURCE& resource) {
	// Check if resource belongs to the context
	if ( context_ != resource.context() ) return false;

	// Try to locate reference in the context
	RESOURCEID reference = resource.reference();
	std::map<RESOURCEID,unsigned int>::iterator it = resources_.find(reference);

	// Handle reference
	if ( it != resources_.end() ) ++(it->second); // If reference was found, increment the number of users
	else resources_.insert( std::pair<RESOURCEID,unsigned int>(reference,1) ); // Otherwise, create the first user
	return true;
}

//! Unreference given resource
/*!
 @param resource The resource to unreference in this context
 @return True if resource was unreferenced, false otherwise
 @note If the resource does not belong to this context, this function always returns false
 @note This function returns false if resource cannot be located in the context
 @note If resource does not have any users any more, it is destroyed for good
 */
template <class RESOURCEID, class CONTEXTID>
template <class RESOURCE>
bool ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext::unreference_resource(const RESOURCE& resource) {
	// Check if resource belongs to the context
	if ( context_ != resource.context() ) return false;

	// Try to locate reference in the context
	RESOURCEID reference = resource.reference();
	std::map<RESOURCEID,unsigned int>::iterator it = resources_.find(reference);

	// If reference was not found, it cannot be unreferenced
	if ( it == resources_.end() ) return false;

	// Remove a user from the reference
	--(it->second);
	if ( it->second <= 0 ) {
		resource.destroy_resource();
		resources_.erase(it);
	}
	return true;
}



//========================================================



//! Reference a new resource
/*!
 @param resource The resource to reference
 @return True if resource was referenced, false otherwise
 */
template <class RESOURCEID, class CONTEXTID>
template <class RESOURCE>
bool ResourceManager<RESOURCEID,CONTEXTID>::reference_resource(const RESOURCE& resource) {
	// Try to locate the resource context
	CONTEXTID context = resource.context();
	std::set<ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext>::iterator it = contexts_.find(context);

	// If there is no such context, create it
	if ( it == contexts_.end() ) it = contexts_.insert(ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext(context)).first;

	// Reference the resource in the context
	return it->reference_resource(resource);
}

//! Unreference a resource
/*!
 @param resource The resource to unreference
 @return True if resource was unreferenced, false otherwise
 */
template <class RESOURCEID, class CONTEXTID>
template <class RESOURCE>
bool ResourceManager<RESOURCEID,CONTEXTID>::unreference_resource(const RESOURCE& resource) {
	// Try to locate the resource context
	CONTEXTID context = resource.context();
	std::set<ResourceManager<RESOURCEID,CONTEXTID>::ResourceContext>::iterator it = contexts_.find(context);

	// If there is no such context, resource cannot be unreferenced
	if ( it == contexts_.end() ) return false;

	// Unreference the resource in the context
	return it->unreference_resource(resource);
}

//! Default constructor
/*!
 *  Defined as private to avoid uncontrolled instances. Call
 *  the ResourceManager::instance() function to access the
 *  unique instance of the resource manager.
 */
template <class RESOURCEID, class CONTEXTID>
ResourceManager<RESOURCEID,CONTEXTID>::ResourceManager() {}

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
